﻿using System;
using System.Diagnostics;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Effects;
using System.ComponentModel;

namespace Oliver.Controls
{
    //[TemplatePart(Name = Coverflow.ItemTemplate, Type = typeof(UIElement))]
    public class Coverflow : Panel
    {
        private const string ItemTemplate = "ItemTemplate";

        UIElement _itemTemplate;

        ContentControl _contentControl;

        public Coverflow()
        {
            //DefaultStyleKey = typeof(Coverflow);

            #region Trick for Keyevents, Panel is not focusable, but contentcontrol is :)
            
            _contentControl = new ContentControl();
            _contentControl.IsTabStop = true;
            _contentControl.KeyDown += new KeyEventHandler(Coverflow_KeyDown);
            
            this.Children.Add(_contentControl);
            _contentControl.Focus();
            
            #endregion

            this.GotFocus += new RoutedEventHandler(Coverflow_GotFocus);
        }

        //public override void OnApplyTemplate()
        //{
        //    base.OnApplyTemplate();

        //    _itemTemplate = this.GetTemplateChild(Coverflow.ItemTemplate) as UIElement;
        //    //Debug.Assert(_layoutRoot != null, "LayoutRoot is null");

        //    //AddItems();
        //    InsertElements();
        //}

        void Coverflow_GotFocus(object sender, RoutedEventArgs e)
        {
            _contentControl.Focus();
        }

        void Coverflow_KeyDown(object sender, KeyEventArgs e)
        {
            Debug.WriteLine("Key pressed");
            
            // Move Back
            if (e.Key == Key.Left)
            {
                if (CurrentItemIndex > 0)
                {
                    CurrentItemIndex--;
                }
            }

            // Move forward
            if (e.Key == Key.Right)
            {
                if (CurrentItemIndex + 1 < this.Children.Count)
                {
                    CurrentItemIndex++;
                }
            }
        }

        #region Properties

        public double OpacityShift
        {
            get { return (double)GetValue(OpacityShiftProperty); }
            set { SetValue(OpacityShiftProperty, value); }
        }

        public static readonly DependencyProperty OpacityShiftProperty =
            DependencyProperty.Register(
                "OpacityShift", 
                typeof(double), 
                typeof(Coverflow), 
                new PropertyMetadata(0.1, OnPropertyChanged));

        public bool DropShadow
        {
            get { return (bool)GetValue(DropShadowProperty); }
            set { SetValue(DropShadowProperty, value); }
        }

        public static readonly DependencyProperty DropShadowProperty =
            DependencyProperty.Register(
                "DropShadow",
                typeof(bool),
                typeof(Coverflow),
                new PropertyMetadata(false, OnPropertyChanged));

        public int SpaceBetweenCovers
        {
            get { return (int)GetValue(SpaceBetweenCoversProperty); }
            set { SetValue(SpaceBetweenCoversProperty, value); }
        }

        public static readonly DependencyProperty SpaceBetweenCoversProperty =
            DependencyProperty.Register(
                "SpaceBetweenCovers", 
                typeof(int),
                typeof(Coverflow), 
                new PropertyMetadata(65, OnPropertyChanged));

        public int MoveDuration
        {
            get { return (int)GetValue(MoveDurationProperty); }
            set { SetValue(MoveDurationProperty, value); }
        }

        public static readonly DependencyProperty MoveDurationProperty =
            DependencyProperty.Register(
                "MoveDuration", 
                typeof(int),
                typeof(Coverflow), 
                new PropertyMetadata(500, OnPropertyChanged));

        public double ItemWidth
        {
            get { return (double)GetValue(ItemWidthProperty); }
            set { SetValue(ItemWidthProperty, value); }
        }

        public static readonly DependencyProperty ItemWidthProperty =
            DependencyProperty.Register(
                "ItemWidth", 
                typeof(double),
                typeof(Coverflow),
                new PropertyMetadata(200.0, OnPropertyChanged));

        public double ItemHeight
        {
            get { return (double)GetValue(ItemHeightProperty); }
            set { SetValue(ItemHeightProperty, value); }
        }

        public static readonly DependencyProperty ItemHeightProperty =
            DependencyProperty.Register(
                "ItemHeight", 
                typeof(double),
                typeof(Coverflow), 
                new PropertyMetadata(400.0, OnPropertyChanged));

        public int CurrentItemIndex
        {
            get { return (int)GetValue(CurrentItemIndexProperty); }
            set { SetValue(CurrentItemIndexProperty, value); }
        }

        public static readonly DependencyProperty CurrentItemIndexProperty =
            DependencyProperty.Register(
                "CurrentItemIndex", 
                typeof(int),
                typeof(Coverflow), 
                new PropertyMetadata(0, OnPropertyChanged));

        public double XShift
        {
            get { return (double)GetValue(XShiftProperty); }
            set { SetValue(XShiftProperty, value); }
        }

        public static readonly DependencyProperty XShiftProperty =
            DependencyProperty.Register(
                "XShift", 
                typeof(double),
                typeof(Coverflow), 
                new PropertyMetadata(0.0, OnPropertyChanged));

        public double YShift
        {
            get { return (double)GetValue(YShiftProperty); }
            set { SetValue(YShiftProperty, value); }
        }

        public static readonly DependencyProperty YShiftProperty =
            DependencyProperty.Register(
                "YShift", 
                typeof(double),
                typeof(Coverflow), 
                new PropertyMetadata(0.0, OnPropertyChanged));

        public double ZShift
        {
            get { return (double)GetValue(ZShiftProperty); }
            set { SetValue(ZShiftProperty, value); }
        }

        public static readonly DependencyProperty ZShiftProperty =
            DependencyProperty.Register(
                "ZShift", 
                typeof(double),
                typeof(Coverflow),
                new PropertyMetadata(-50.0, OnPropertyChanged));

        public double RotationAngle
        {
            get { return (double)GetValue(RotationAngleProperty); }
            set { SetValue(RotationAngleProperty, value); }
        }

        public static readonly DependencyProperty RotationAngleProperty =
            DependencyProperty.Register(
                "RotationAngle", 
                typeof(double),
                typeof(Coverflow),
                new PropertyMetadata(50.0, OnPropertyChanged));

        public double SpaceBetweenCenter
        {
            get { return (double)GetValue(SpaceBetweenCenterProperty); }
            set { SetValue(SpaceBetweenCenterProperty, value); }
        }

        public static readonly DependencyProperty SpaceBetweenCenterProperty =
            DependencyProperty.Register(
                "SpaceBetweenCenter", 
                typeof(double),
                typeof(Coverflow),
                new PropertyMetadata(10.0, OnPropertyChanged));

        private static void OnPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            ((Coverflow)sender).InvalidateArrange(); 
        }

        #endregion

        protected override Size ArrangeOverride(Size finalSize)
        {
            InsertElements();
            return base.ArrangeOverride(finalSize);
        }

        private void InsertElements()
        {
            foreach (var item in this.Children)
            {
                if (item.Visibility == Visibility.Visible)
                {
                    if (this.DropShadow)
                    {
                        DropShadowEffect dse = new DropShadowEffect();
                        dse.BlurRadius = 5;
                        item.Effect = dse;
                    }
                    else
                    {
                        item.Effect = null;
                    }
                    RenderItem(item);
                    item.MouseLeftButtonDown += libItem_MouseLeftButtonDown;
                }
            }
        }

        private void RenderItem(UIElement item)
        {
            int itemIdx = this.Children.IndexOf(item);
            
            // Größe
            FrameworkElement fe = item as FrameworkElement;
            fe.Width = this.ItemWidth;
            fe.Height = this.ItemHeight;

            // Center
            double centerX = (double)this.Width / 2 - ItemWidth / 2;
            double centerY = (double)this.Height / 2 - ItemHeight / 2;

            TranslateTransform tt = new TranslateTransform();
            tt.X = centerX;
            tt.Y = centerY;
            item.RenderTransform = tt;

            // Opacity
            double opacity = 1;

            // Angle
            PlaneProjection pp = new PlaneProjection();
            if (itemIdx < this.CurrentItemIndex)
            {
                // Item is Left of center
                pp.RotationY = -this.RotationAngle;
                pp.LocalOffsetX = 
                    (XShift + SpaceBetweenCovers) * (itemIdx - this.CurrentItemIndex)
                    - (ItemWidth / 2)
                    - SpaceBetweenCenter
                    - ZShift;
                pp.LocalOffsetZ = (this.CurrentItemIndex - itemIdx) * -ZShift;
                pp.LocalOffsetY = (this.CurrentItemIndex - itemIdx) * -YShift;
                opacity = 1-(this.CurrentItemIndex - itemIdx) * OpacityShift;
            }
            else if (itemIdx > this.CurrentItemIndex)
            {
                // Item is Right of center
                int pos = itemIdx - this.CurrentItemIndex;
                pp.RotationY = this.RotationAngle;
                pp.LocalOffsetX =
                    (XShift + SpaceBetweenCovers) * pos
                    + (ItemWidth / 2)
                    + SpaceBetweenCenter
                    + ZShift;
                pp.LocalOffsetZ = pos * -ZShift;
                pp.LocalOffsetY = pos * -YShift;
                opacity = 1- pos * OpacityShift;
            }
            else
            {
                // Item is Center
                pp.RotationY = 0;
                pp.LocalOffsetX = 0;
                pp.LocalOffsetY = 0;
                opacity = 1;
            }

            if (item.Projection == null)
            {
                item.Projection = new PlaneProjection();
            }

            // Create a duration of x seconds.
            TimeSpan ts = TimeSpan.FromMilliseconds(this.MoveDuration);
            Duration duration = new Duration(ts);

            Storyboard sb = new Storyboard();

            #region Rotation Y

            // Rotation Y
            DoubleAnimation daRotationY = new DoubleAnimation();
            daRotationY.Duration = duration;
            PropertyPath propertyPathRotationY = new PropertyPath("(UIElement.Projection).(PlaneProjection.RotationY)");
            Storyboard.SetTarget(daRotationY, item);
            Storyboard.SetTargetProperty(daRotationY, propertyPathRotationY);
            daRotationY.To = pp.RotationY;
            sb.Children.Add(daRotationY);

            #endregion

            #region Local Offset X

            DoubleAnimation daLocalOffsetX = new DoubleAnimation();
            daLocalOffsetX.Duration = duration;
            PropertyPath propertyPathLocalOffsetX = new PropertyPath("(UIElement.Projection).(PlaneProjection.LocalOffsetX)");
            Storyboard.SetTarget(daLocalOffsetX, item);
            Storyboard.SetTargetProperty(daLocalOffsetX, propertyPathLocalOffsetX);
            daLocalOffsetX.To = pp.LocalOffsetX;
            sb.Children.Add(daLocalOffsetX);

            #endregion

            #region Local Offset Z

            DoubleAnimation daLocalOffsetZ = new DoubleAnimation();
            daLocalOffsetZ.Duration = duration;
            PropertyPath propertyPathLocalOffsetZ = new PropertyPath("(UIElement.Projection).(PlaneProjection.LocalOffsetZ)");
            Storyboard.SetTarget(daLocalOffsetZ, item);
            Storyboard.SetTargetProperty(daLocalOffsetZ, propertyPathLocalOffsetZ);
            daLocalOffsetZ.To = pp.LocalOffsetZ;
            sb.Children.Add(daLocalOffsetZ);

            #endregion

            #region Local Offset Y

            DoubleAnimation daLocalOffsetY = new DoubleAnimation();
            daLocalOffsetY.Duration = duration;
            PropertyPath propertyPathLocalOffsetY = new PropertyPath("(UIElement.Projection).(PlaneProjection.LocalOffsetY)");
            Storyboard.SetTarget(daLocalOffsetY, item);
            Storyboard.SetTargetProperty(daLocalOffsetY, propertyPathLocalOffsetY);
            daLocalOffsetY.To = pp.LocalOffsetY;
            sb.Children.Add(daLocalOffsetY);

            #endregion

            #region Opacity

            DoubleAnimation daOpacity = new DoubleAnimation();
            daOpacity.Duration = duration;
            PropertyPath propertyPathOpacity = new PropertyPath("(UIElement.Opacity)");
            Storyboard.SetTarget(daOpacity, item);
            Storyboard.SetTargetProperty(daOpacity, propertyPathOpacity);
            daOpacity.To = opacity;
            sb.Children.Add(daOpacity);

            #endregion 

            fe.UpdateLayout();

            // Begin the animation.
            sb.Completed += sb_Completed;
            sb.Begin();

            
        }

        void sb_Completed(object sender, EventArgs e)
        {
            Storyboard sb = sender as Storyboard;
            sb.Completed -= sb_Completed;
        }

        void libItem_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            int idx = this.Children.IndexOf(sender as UIElement);
            this.CurrentItemIndex = idx;
            this.InvalidateArrange();
        }
    }
}